/* INCLUDES ******************************************************************/
#include "ses_adc.h"
#include "ses_common.h"
#include "ses_uart.h"
/* DEFINES & MACROS **********************************************************/

#define ADC_TEMP_TABLE_SIZE  (sizeof(adcTempTable) / sizeof(uint16_t))
#define ADC_TEMP_MIN           0    /* minimum temp [dC]      */
#define ADC_TEMP_MAX		  50	/* maximum temp [dC]	  */
#define ADC_TEMP_STEP         50    /* stepping in table [dC] */

/* PRIVATE VARIABLES *********************************************************/

static const uint16_t adcTempTable[] =
	{ 888, 762, 655, 560, 479, 410, 351, 301, 259, 223, 192 };

static const uint16_t celciusTable[] =
	{   0,   5,  10,  15,  20,  25,  30,  35,  40,  45,  50 };


/* FUNCTION DEFINITION *******************************************************/


// TODO ADLAR bit, which should set the ADC result right adjusted.
void adc_init(void) {
	//  set to input
	DDR(ADC_MIC_NEG_PORT) &= ~(1 << ADC_MIC_NEG_PIN);
	DDR(ADC_MIC_POS_PORT) &= ~(1 << ADC_MIC_POS_PIN);
	DDR(ADC_TEMP_PORT) &= ~(1 << ADC_TEMP_PIN);
	DDR(ADC_LIGHT_PORT) &= ~(1 << ADC_LIGHT_PIN);
	DDR(ADC_JOYSTICK_PORT) &= ~(1 << ADC_JOYSTICK_PIN);

	// pull up bit
	ADC_MIC_NEG_PORT &= ~(1 << ADC_MIC_NEG_PIN);
	ADC_MIC_POS_PORT &= ~(1 << ADC_MIC_POS_PIN);
	ADC_TEMP_PORT &= ~(1 << ADC_TEMP_PIN);
	ADC_LIGHT_PORT &= ~(1 << ADC_LIGHT_PIN);
	ADC_JOYSTICK_PORT &= ~(1 << ADC_JOYSTICK_PIN);

	// deactivate the pr bit
	PRR0 &= ~(1 << PRADC);

	// set reference voltage
	ADMUX |= ADC_VREF_SRC;

	// TODO initialise the ADMUX register
	//ADMUX &= ~ (1<< ADLAR);

	// configure the ADCSRA register
	ADCSRA |= ADC_PRESCALE;
	ADCSRA |= (1 << ADEN);

	// disable auto trigger
	ADCSRA &= ~(1 << ADATE);

	// disable conversion
	ADCSRA &= ~(1 << ADSC);
}

uint16_t adc_read(uint8_t adc_channel) {

	if (adc_channel >= ADC_NUM) {
		return 0;
	}

	// set first 3 bits to 0
	ADMUX = (ADMUX & (ADMUX_MASK));

	// apply adc_channel
	ADMUX |= adc_channel;

	// start conversion
	ADCSRA |= (1 << ADSC);

	// wait until reading finished
	while ((ADCSRA & (1 << ADSC))) {
		asm("nop");
	}

	return (ADC);
}

void adc_disable(void) {
	// deactivate adc
	ADCSRA &= ~(1 << ADEN);

	// activate the pr bit
	PRR0 |= (1 << PRADC);
}

/* NOTE This only works in case of monotonically descending values in adcTempTable */
int16_t adc_convertTemp(uint16_t val) {
	uint8_t i;


	for (i = 0; i != ADC_TEMP_TABLE_SIZE; i++) {
		// case lowest temperature
		if (((&adcTempTable[i - 1] == NULL) && (&adcTempTable[i] != NULL)))
			return 0;

		// case normal linear interpolation
		else if ((&adcTempTable[i - 1] != NULL) && (&adcTempTable[i] != NULL)) {


			if ((val >= adcTempTable[i]) && (val < adcTempTable[i - 1]))
				return ((uint16_t) ((double) (val - adcTempTable[i])
						/ (double) (adcTempTable[i - 1] - adcTempTable[i])
						* (double) (celciusTable[i] - celciusTable[i - 1]))
						+ celciusTable[i - 1]);

		// case highest temperature
		} else if (((&adcTempTable[i - 1] != NULL) && (&adcTempTable[i] == NULL)))
			return 50;
	}

return 0;
}

